# 面试现场:小伙伴美团一面的分享和分析(含解答)
作者:小傅哥
博客:https://bugstack.cn (opens new window)
沉淀、分享、成长,让自己和他人都能有所收获!😄
# 一、前言
最近给大家整理一些面试现场,真实的面试题和答案以及总结
虽然技术的知识点是固定的,但不同大厂不同面试官的提问方式却不一样。一方面是你的简历的内容影响,另外一方面是受面试官的个人经历导致。最终就看你们是否对脾气了,如果不对换个部门重新投
但如果你能把一个知识点真的搞懂,是真的懂。那么,无论面试官怎么问,其实都能回答的很精彩,精彩决定了你的职位和薪资。
为了让大家在以后的面试中都能精彩,像秀一样,特此准备整理一些真实的面试现场分享给大家,希望对每一个求职时都有帮助。不过这些都是底层技术的学习帮助,如果你希望自己更牛,那么要多学习、多积累!
如果本文涉及的 Java 核心内容不能很好的理解,可以阅读小傅哥的《Java 面经手册》 (opens new window),全书共5章29节,417页11.5万字,耗时4个月完成。涵盖数据结构、算法逻辑、并发编程、JVM以及简历和互联网大厂面试等内容。同时此书并不是单纯的面试题,也不是内卷八股文。而是从一个单纯的和程序员有关的数学知识点开始,深入讲解 Java 的核心技术。
# 二、面试现场
背景
小伙伴面的是美团,求职方向是团队技术负责人。此次面试以连环追问的方式为主,喜欢刨根问底,非常考验面试者的技术功底。以下是整理出的部分面试题
,我们看看这些题该如何回答。
先让写个线程安全的单例模式
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance(){
if(null != instance) return instance;
synchronized (Singleton.class){
if (null == instance){
instance = new Singleton();
}
}
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- 考察线程安全的单例模式,一方面是对基础的了解,另一方面以单例模式作为入口考察并发编程的知识点。
- 在这里你要知道,并发编程的三要素包括:原子性、可见性、有序性。
- 那具备三要素知识点的单例模式,主要是如上案例中的
双重检查锁
和CAS忙等策略。而双重检查锁的单例方式,对于面试官和求职者来说,可以往下继续聊的点就有了。 - 当然,如果你没有写出这样一种单例模式,大概率会被面试官引导过来。在面试的过程中最好的方式是主动和引导面试官走,也称对脾气,也就是技术臭味相投的感觉。
高并发下,单例模式会存在哪些问题?
- 单例模式就是以防止线程不安全和提高代码执行效率而设计的。
- 双重锁定检查(DCL,Double Check Lock),也就是为此设计的。别被 DCL 唬住喽
可见性和指令重排是怎么回事?
- 可见性,Java 编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排它锁单独获得这个变量。
- 指令重排,在程序执行过程中为了性能考虑,,编译器和 CPU 可能会对指令重新排序。对于并发多线程场景下,指令重排会产生不确定的执行效果。
voliate 关键字为什么可以解决可见性和指令重排?
可见性
- 首先,volatile 关键字修饰的共享变量可以提供这种可见性规范,也叫做读写可见。
- 被 volatile 关键字修饰的共享变量在转换成汇编语言时,会加上一个以 lock 为前缀的指令,当CPU发现这个指令时,立即将当前内核高速缓存行的数据回写到内存,同时使在其他内核里缓存了该内存地址的数据无效。
- 另外,在早期的 CPU 中,是通过在总线加 LOCK# 锁的方式实现的,但这种方式开销较大。所以Intel开发了缓存一致性协议,也就是 MESI 协议,该解决缓存一致性。
volatile 的好处
,volatile 是一种非锁机制,这种机制可以避免锁机制引起的线程上下文切换和调度问题。所以,volatile 的执行成本比 synchronized 更低。volatile 的不足
,volatile 关键字只能保证可见性,不能保证原子性操作。- 此外如果你还能回答出,Unsafe.loadFence(); 保证在这个屏障之前的所有读操作都已经完成。的一些使用,那么面试绝对可以加分。
指令重排
public volatile boolean sign;
descriptor: Z
flags: ACC_PUBLIC, ACC_VOLATILE
1
2
3
2
3
- 从字节码层面,添加 ACC_VOLATILE,在汇编指令的打印会有
lock addl $0x0,(%rsp)s
- 从 JVM 层面,JMM 提供了8个 Happen-Before 规则来约束数据之间竞争、4个内存屏障 (LL LS SL SS)和As-if-serial
- 从硬件层面,sfence、lfence、mfence
除了双重检查锁之外,还有什么别的办法实现线程安全的单例模式。
public enum Singleton {
INSTANCE;
public void test(){
System.out.println("hi~");
}
}
@Test
public void test() {
Singleton_07.INSTANCE.test();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- 如果再让写一种差异比较大,方式不太一样的单例模式,那么 Effective Java 作者推荐使用枚举的方式这个时候可以甩出来秀一波了。
- 这种方式解决了最主要的;线程安全、自由串行化、单一实例。
你看过什么源码吗?JVM 的源码是否看过?
- 看过Dubbo、Spring、Mybatis等框架和一些中间件的源码,关于 JVM 多线程、并发、锁等核心内容的源码有所了解。
- 其实这块问你的内容,主要考察你对技术的学习是否有核心深度。如果你能对此有所回答,那么会得到面试官的一定认可。
Dubbo是怎么工作的?
- 简单来说,引入 Dubbo 服务的接口提供方与接口消费方,通过注册与拉取接口信息,把服务双方通过 Socket 进行连接。之后接口通信的时候通过代理类传输 Socket 请求,再由接口提供方通过反射调用真实服务,最终把接口执行信息返回给调用端。
- 在整个的回答过程中,可能会聊到注册中心、代理、反射、通信模型以及 Netty 的相关知识,这主要包括了通信协议的定义、半包粘包以及流量整形和各类知识。
Dubbo通信是用的什么协议?
- Dubbo 主要支持这些协议:dubbo://、rmi://、hessian://、http://、webservice://、thrift://
- rmi 协议:走java二进制序列化,多个短连接,适合消费者和提供者数量差不多,适用于文件的传输,一般较少用
- dubbo协议: 默认就是走此协议,单一长连接,NIO 异步通信,基于 hessian 作为序列化协议
- hessian协议:走 hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多,适用于文件的传输,一般较少用
- http协议:走json序列化
- 此外序列化的方式还有protobuf,protobuf 全称 Google Protocol Buffers,是 google 开发的一套用于数据存储,网络通信时用于协议编解码的工具库。
9 . 属于网络传输中的哪一层?
- TCP/IP 四层模型,是 OSI 七层模型的简化,泛指众多(TCP,UDP,IP等)协议。
- Dubbo 协议是 TCP 协议之上的协议,采用单一长连接和 NIO 异步通讯。
TCP/IP协议来说建立连接的时候,为什么需要三次握手?
- 三次握手是在安全可靠的基础上最少握手次数的方案,而两次握手并不能保证可靠性,四次握手又浪费了传输效率。
- TCP 传输控制协议,是一个面向连接的协议。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。对于这部分知识可以深入了解下,资料也很丰富。
你刚才说你们公司用的是Kafka,那么你觉得什么场景下要用到Kafka呢?
特性 | Kafka | RocketMQ | ActiveMQ | RabbitMQ |
---|---|---|---|---|
吞吐量 | 高吞吐量,可达 10w 级别 | 高吞吐量,可达 10w 级别 | 1w 级别,吞吐量相交比较低 | 1w 级别,吞吐量相交比较低 |
时效性 | 延迟在 ms 级 | 延迟在 ms 级 | 延迟在 ms 级 | 延迟在微妙级,延迟最低 |
可用性 | 天然的分布式系统,数据有副本机制,可用性非常高 | 分布式架构,可用性非常高 | 主从架构,可用性较高 | 同 ActiveMQ |
维护性 | 基于 Java 和 Scala 语言 实现,社区活跃度高,维护成本较低 | 基于 Java 语言实现,社区活跃度高,维护成本较低 | 基于 Java 语言实现,消息队列场景功能很完备,但社区活跃度较低,维护成本较高 | 基于 erlang 语言开发,社区活跃度一般,小团队维护成本较高 |
- 首先要知道,开源社区有很多优秀的队列中间件,比如 RabbitMQ、ActiveMQ、RocketMQ、Kafka,有些大厂还有自研的 MQ 队列中间件。
- 解耦、削峰、驱动等场景下的业务时以及日志采集系统、监控系统、流式处理等各类场景中都离不开MQ队列。
你觉得对于一个消息中间件来说,需要达到哪些要求?
- 高吞吐、低延时、可用性和可维护性,是消息队列中间件的核心要求。
- 同时你可以说出,中间件的作用是解决共性凝练和复用,从本质上提升代码的复用性、松耦合和互操作的标准机制,从而提升研发整体交付效率。
Kafka是如何做到高吞吐量的?
- Kafka是分布式消息系统,需要处理海量的消息,Kafka的设计是把所有的消息都写入速度低容量大的硬盘,以此来换取更强的存储能力,但实际上,使用硬盘并没有带来过多的性能损失。主要围绕顺序读写、零拷贝、文件分段、批量发送和数据压缩几个方面提高吞吐率。
接下来是关于一些项目的问题
- 在公司做过哪些项目,主要是哪方面?基础中间件还是业务项目?这个项目的主要难点在哪里?怎么解决的?
- 项目做到现在,有没有一些数据上的评价指标,表示这个项目做得不错?如果没有,那么现在让你自己评价一下,你觉得应该从哪些方面做考量?
- 对于一个系统来说,如何确保它的稳定性?(线程池用的哪种?为什么要用这几种?怎么用的?)
- 你在项目当中扮演什么样的角色?系统Owner还是开发?
- 你们团队的人员分布是怎么样的?内编几个,外包几个?如果带外包的话,如何管理外包?
面试复盘
- 基础知识要牢固,JVM内存分布,多线程,基本是面试必问的。其中多线程并发的源头:可见性,有序性,原子性相关的知识点一定要深入理解,能讲多清楚就讲多清楚,因为这些涉及到CPU,内存,指令集等底层知识,是多线程理论的源头。这方面的东西,如果面试官本身具备一定实力的话,是很容易深究下去的,如果只是浅尝辄止,一知半解,很容易暴露自己底层知识的不牢。逼格就会降低一个大的档次。
- 要有一定动手能力,一些常见的设计模式要随时随地可以写出来,如果有多种方案实现的话,需要都讲出来,并指出其中的优缺点。
- 分布式理论要扎实,要深入理解常见RPC框架(如dubbo)和消息队列的设计和实现原理。一个框架的出现,以及平时业务开发所遇不到的各种高阶知识点(比如dubbo的重试机制,熔断,降级服务等,kafka相比ActiveMQ等其它消息队列,优势在哪里,为什么它可以支撑那么高的吞吐量等等),一定是为了解决某些特定问题才会出现的,了解了问题源头,才能更好理解框架的全局设计思想,然后才能更好地吸收框架里面的各种解决方案以及背后体现出来的思想。
- 网络基础要夯实,http,https,TCP/IP协议以及网络通信的基础知识还是要知道的。
- 实际项目一定要学会总结,让人看到你做项目的亮点(代码规范,可维护性,可扩展性,稳定性,性能优化,访问量,以及一些其它的数据指标)。如果没有,一定要想办法自己加入一些可信的东西出来。毕竟面试官无法验证你说的是真是假,如果说得很像那么回事,面试官应该还是会认可的。
# 三、总结
- 如果你不希望面试时自己的薪资、待遇、职位,被别人安排来摆弄去,就要不断的在开发过程中提炼、总结、归纳自己的知识项,找到个人的价值力量,不断的去扩宽和加深。
- 我特别同意本文我的小伙伴的总结,也是此次求职者的分享;“程序员不是一个木偶人,不只是被动的接需求或者任务,也不能只是埋头苦干”。公司是没有永远稳定的,互联网的变化也更多,只有自己拥有了留下的本事和走出去的能力,才是个人的最安全区域。
- 最后希望每一个研发小伙伴都能有所积累和沉淀,把那些视频当电影、看文章当小说的毛病改改,要学会动起来,验证起来,实践才能让一个知识更能落到自己的脑子里。